package com.example.demo.util;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.openssl.PEMReader;

import javax.crypto.Cipher;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author Zeng
 */
public class RSAUtils {

    private static final String PROVIDER_NAME = "BC";

    public final static String MAP_KEY_PUBLIC_KEY = "publicKey";
    public final static String MAP_KEY_PRIVATE_KEY = "privateKey";

    /**
     * 数字签名算法:SHA1withRSA
     **/
    public final static String SIGNATURE_ALGORITHM_SHA1WITHRSA = "SHA1withRSA";
    public final static String SIGNATURE_ALGORITHM_SHA256WITHRSA = "SHA256withRSA";
    /**
     * 数字签名算法:MD5withRSA
     **/
    public final static String SIGNATURE_ALGORITHM_MD5WITHRSA = "MD5withRSA";

    /**
     * 生成RSA公私钥对<br/>
     * Map.keySet() ： <br/>
     * 公钥：{@link RSAUtils#MAP_KEY_PUBLIC_KEY} <br/>
     * 私钥：{@link RSAUtils#MAP_KEY_PRIVATE_KEY}
     *
     * @return
     */
    public static Map<String, String> generateRSAKeyPlain(int keysize) {
        Map<String, String> map = new HashMap<String, String>();
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(keysize);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            map.put(MAP_KEY_PUBLIC_KEY, getPublicKeyPlain(keyPair.getPublic()));
            map.put(MAP_KEY_PRIVATE_KEY, getPrivateKeyPlain(keyPair.getPrivate()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 生成RSA公私钥对<br/>
     * Map.keySet() ： <br/>
     * 公钥：{@link RSAUtils#MAP_KEY_PUBLIC_KEY} <br/>
     * 私钥：{@link RSAUtils#MAP_KEY_PRIVATE_KEY}
     *
     * @return
     */
    public static Map<String, String> generateRSAKeyPlain() {
        Map<String, String> map = new HashMap<String, String>();
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            map.put(MAP_KEY_PUBLIC_KEY, getPublicKeyPlain(keyPair.getPublic()));
            map.put(MAP_KEY_PRIVATE_KEY, getPrivateKeyPlain(keyPair.getPrivate()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * RSA加密
     *
     * @param key  公钥
     * @param data 数据
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(Key key, byte[] data) throws Exception {
        return encrypt(key, "RSA/ECB/PKCS1Padding", data);
    }

    /**
     * RSA加密
     *
     * @param key            加密
     * @param transformation 算法
     * @param data           数据
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(Key key, String transformation, byte[] data) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance(transformation, PROVIDER_NAME);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            int blockSize = cipher.getBlockSize();
            int outputSize = cipher.getOutputSize(data.length);
            int leavedSize = data.length % blockSize;
            int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
                    : data.length / blockSize;
            byte[] raw = new byte[outputSize * blocksSize];
            int i = 0;
            while (data.length - i * blockSize > 0) {
                if (data.length - i * blockSize > blockSize) {
                    cipher.doFinal(data, i * blockSize, blockSize, raw, i
                            * outputSize);
                } else {
                    cipher.doFinal(data, i * blockSize, data.length - i
                            * blockSize, raw, i * outputSize);
                }
                i++;
            }
            return raw;
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * RSA解密
     *
     * @param data
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(Key key, byte[] data) throws Exception {
        return decrypt(key, "RSA/ECB/PKCS1Padding", data);
    }

    /**
     * RSA解密
     *
     * @param key
     * @param transformation
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(Key key, String transformation, byte[] data) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance(transformation, PROVIDER_NAME);
            cipher.init(Cipher.DECRYPT_MODE, key);
            int blockSize = cipher.getBlockSize(); //密文数据分块
            if (data.length % blockSize != 0)
                throw new Exception("RSA descrypt:unexpected data length:" + data.length);
            int blocks = data.length / blockSize; //获得分块数据
            int inputOffset = 0;
            int outputOffset = 0;
            byte[] buffer = new byte[blocks * blockSize];
            for (int i = 0; i < blocks; i++) {
                int hasDone = cipher.doFinal(data, inputOffset, blockSize, buffer, outputOffset);
                outputOffset += hasDone;
                inputOffset += blockSize;
            }
            return ArrayUtils.subarray(buffer, 0, outputOffset);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 数据签名验签
     *
     * @param publicKey 公钥
     * @param algorithm 数字签名算法  "MD5withRSA","SHA1withRSA" 等
     * @param data      数据
     * @param signData  验签数据
     * @return true if the signature was verified, false if not.
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws SignatureException
     * @throws Exception
     */
    public static boolean verify(PublicKey publicKey, String algorithm, byte[] data, byte[] signData) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature signature = Signature.getInstance(algorithm);
        signature.initVerify(publicKey);
        signature.update(data);
        return signature.verify(signData);
    }

    /**
     * 进行数字签名
     *
     * @param privateKey 私钥
     * @param data       签名数据
     * @param algorithm  数字签名算法  "MD5withRSA","SHA1withRSA","SHA256withRSA" 等
     * @return the signature bytes of the signing operation's result.
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws SignatureException
     * @throws Exception
     */
    public static byte[] sign(PrivateKey privateKey, byte[] data, String algorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature signature = Signature.getInstance(algorithm);
        signature.initSign(privateKey);
        signature.update(data);
        return signature.sign();
    }

    /**
     * 获取公钥Base64编码的字符串
     *
     * @param publicKey
     * @return
     */
    public static String getPublicKeyPlain(PublicKey publicKey) {
        byte[] pbk = publicKey.getEncoded();
        return Base64.encodeBase64String(pbk);
    }

    /**
     * 获取私钥Base64编码的字符串
     *
     * @param privateKey
     * @return
     */
    public static String getPrivateKeyPlain(PrivateKey privateKey) {
        byte[] prk = privateKey.getEncoded();
        return Base64.encodeBase64String(prk);
    }

    /**
     * Base64编码的字符串 转 公钥对象
     *
     * @param publicKeyStr
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey loadPublicKey(String publicKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] buffer = Base64.decodeBase64(publicKeyStr);
        PublicKey publicKey = null;
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
        publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * Base64编码的字符串 转 私钥对象
     *
     * @param privateKeyStr
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey loadPrivateKey(String privateKeyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PrivateKey privateKey = null;
        byte[] buffer = Base64.decodeBase64(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    public static PrivateKey loadPrivateKeyFromPem(InputStream pemInputStream) throws IOException {
        PEMReader pemReader = new PEMReader(new InputStreamReader(pemInputStream));
        KeyPair keyPair1 = (KeyPair) pemReader.readObject();
        return keyPair1.getPrivate();
    }

    public static PublicKey loadPublicKeyFromPem(InputStream pemInputStream) throws IOException {
        PEMReader pemReader = new PEMReader(new InputStreamReader(pemInputStream));
        PublicKey publicKey = (PublicKey) pemReader.readObject();
        return publicKey;
    }


    public static void main(String[] args) throws InterruptedException {
        String sign = "Uio22xlcVZr7NidcueWPYIiOIsn/EWgEvku59eOzVGUDjTSgrakmwUBO2ko7zr7M5R6C9RA/au4YClNNeCxrBdikeqVTcr+VVStek0TyxzHgb2M46OFn0FwUJQFhASWSikCj8JowJ1bcclB/D9XcbQdzT7BF2GJ6Md6VMkb1kp849a6oliMhV67zJbuGY4IPX4bRv/npVUejH/1jl2dQAzaBFRYKH3xJlzrNUaSVwWuEFL8//Co12cpEm0eAJGlg+IJ7cMK0rQwvxFZSezx2RCHnbBWRDzGLdWI9Ulk7HZJylO460UAnO1fCTCuGZ5QjpPIKrM7FPfD5yovu4Nm9ZA==";
        String data = "MCMS_DATE=2018-10-26 14:52:30&PARTNER_ID=3E1E365891D8469C82938E718FDFA404&SIGN_TYPE=SHA256withRSA&accountToken=88662140257240447150&amount=100.0&devSn=00000304N9NL00000001&outOrderNo=A12345678930&transNo=00000030";
        String priKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCGO7G/HM++VrT6w/OCyIo8z64mLFl/17SEQS/2uJiBotynCrR1a3PqKAXtVXpLoCRR0daIy69tnUiPwTag+RdAzDKCfTsIbUrpsNEihYVb7RANsRsgDxxS542rIT6LLkqBrlAhikQXuWozk8JgM1QJRV3FEc4JrSPscc0p6G0l0bDrCxXB3TsLAmhqarIh7vpycWs3XO6prNAoj/mwMyy8AVf6jlQV/lZ6HD8tS8iJQ0o+jz0xRpBVkoJZmEZ1FIT/O/q5NTy3chjJSHG9Byqo0xS564X3bnCDixndj6IirkCs2LAacCvuY9opd/25vowSfmgpx/2ltjUp7EF5jVo9AgMBAAECggEANgeTDPTA3cEyyXjT7EFh+wYw96aEQ8QEIfPHJYy5vfxaylWxu02L86U2dYghWI1V8SUAMEGkE//p9Ar2PSbl6CwI7KMRftvW+mc0VqvduNmby5cM7wOGCAyx5j7UWNtCHB6YnFd1/wKfr7D63RPr8GteSfNXtmddfU91IDgwrra1dbgoHN8/aQLBjIUanXBQ5qfextIGUIBhGu8usM8tLIqgRSY/6OS8yxhFPWBo1Pg3mWHizRhexl5ti0uqdM5cD0tvQSNRr9yzJN5575uHpac76EZAabvHosLGnPgXVidR0MG/I4AtxKdrdFjh0FzPmAQM/0Eg1pBKOLb6LRpHgQKBgQDup3t3xg1D3dlqWACGT24PcVtVFXIaNx+VoVCdXPyMwdx/mcG3nLj/DZlK/Blh8s+kb4dt3rrcvPkA5QN9houEeR94NtIaH9pDRFgeQa3GOluYNQEXaKXtf4PLEJY5FlhciZQbd1iHDcqR9XBos4j+PbRD7unx7/tYuu903wCvYQKBgQCP/U2CqidTDFZWSO3pOTMD4KzB3Cb38S5yXbVpgQzHSKqgTn29mqiOOlZx5oNTfDW087Crb0eV6XeC0RWRiuOirFVVy1Ty1WiBFT3R520AxbQOwRVR5mK3OMKEcOOrO1aya36TrIWxT4f/N27FQRJf/OmJO7/Qko8TWjK4NCQkXQKBgE4KmLZ/HmFeD7XV1z4UhWIpaHt3StvFwN9WZB1BhhilYGydpBpd9qT5FnLN46MDGuGj0v6bwjFi3m5rdWu77q3ASgkurB/84Iv0sngadDAJb2BjnNrBztjORRPHdchvRS/lkXg12842NVLrlvm2FAtHtEYuv3PtTR0j7CAkuH2hAoGAa+Z+qhcGJvbM0BYQqr/hWduIujFWxmBm1O8iHfDF+aSbsEuBcI8wFnzjALGj4fDncjVXQ/C87TsqAs7Z4dn5oZKQ1BzuCJchm/GmBODuzzBYMOJCLsB9Yx3WrlbGVS+/VS62iMU03l1h5T/N82VC9slY3Cz5HbxmGTNzZJQ2LUUCgYEAv8c+hU814dr89TrKKFWKaZL+ZZNeC71OciKHvfH2fjoNKkshyg5VPkYTGfFoE2r7lTiBcGPU45eeYzc229FMW/kZyIigLPR5NOJf/4fxgP4dgi88IIDje29vEETSq5SoH/+AIB0890SoojRdz5EcmFAlJGy4akPIjN0j8yxR2ng=";
        try {
            byte[] r = sign(loadPrivateKey(priKey), data.getBytes(), SIGNATURE_ALGORITHM_SHA256WITHRSA);
            String signstr = Base64.encodeBase64String(r);
            System.out.println("SIGN:" + signstr);
            boolean b = verify(loadPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhjuxvxzPvla0+sPzgsiKPM+uJixZf9e0hEEv9riYgaLcpwq0dWtz6igF7VV6S6AkUdHWiMuvbZ1Ij8E2oPkXQMwygn07CG1K6bDRIoWFW+0QDbEbIA8cUueNqyE+iy5Kga5QIYpEF7lqM5PCYDNUCUVdxRHOCa0j7HHNKehtJdGw6wsVwd07CwJoamqyIe76cnFrN1zuqazQKI/5sDMsvAFX+o5UFf5Wehw/LUvIiUNKPo89MUaQVZKCWZhGdRSE/zv6uTU8t3IYyUhxvQcqqNMUueuF925wg4sZ3Y+iIq5ArNiwGnAr7mPaKXf9ub6MEn5oKcf9pbY1KexBeY1aPQIDAQAB"),
                    SIGNATURE_ALGORITHM_SHA256WITHRSA, data.getBytes(), Base64.decodeBase64(sign));
            System.out.println(b);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (SignatureException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
//        long time = System.currentTimeMillis();
//        System.out.println(time);
//        System.out.println(1426212000000l);
//        for (int i = 0; i < 10; i++) {
//
//            long t = (time << 22) + (50 << 12) + 100;
//            Thread.sleep(50l);
//            System.out.println(t);
//        }

        Map<String, String> keyPairs = generateRSAKeyPlain(2048);
        System.out.println("PARTNER_ID:" + UUID.randomUUID().toString().toUpperCase().replace("-", ""));
        System.out.println("公钥：");
        System.out.println(keyPairs.get(MAP_KEY_PUBLIC_KEY));
        System.out.println("私钥：");
        System.out.println(keyPairs.get(MAP_KEY_PRIVATE_KEY));

    }
}
